﻿using Microsoft.Win32;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.ServiceProcess;
using System.Text;
using System.Timers;
using WindowService;

namespace ConsoleHardwareMonitor
{
    public class Service : ServiceBase
    {
        
        protected Ini ini = null;
        private Wmi wmi = null;
        private IOHelper ioHelper = null;
        private HardwareHelper hardwareHelper = null;
        private NetHelper netHelper = null;
        private HttpServer httpServer = null;

        //心跳上送定时器
        private Timer heartbeatTimer = null;
        //全状态数据采集定时器
        private Timer allStateCollectTimer = null;
        //安防警报定时器
        private Timer warningTimer = null;
        //网络诊断定时器
        private Timer netCheckTimer = null;
        private int _GetLinkStateTimes = 0;
        //心跳检测断线计数, 超过三次进行重拨
        private int _HeartbeatFailedTimes = 0;

        private static string serviceName = "CHardwareMonitor";
        public string currentDirectory = Environment.CurrentDirectory;
        public string mac = "";
        protected void initLog()
        {
            try
            {
                //获取默认服务程序地址
                currentDirectory = getCurrentDirectory();

                ini = new Ini(currentDirectory, @"config.ini", this.getDefaultConfigIniContent());
                string logSavePath = this.ini.ReadValue("Setting", "LogSavePath") as string;
                Log.SavePath = logSavePath == "" ? currentDirectory : logSavePath;
                Log.needTrace = getShowTrace();
            }
            catch (Exception e)
            {
                Log.WriteLog("MainService Init Log failed, error: " + e.Message, LogType.Error);
            }
        }
        protected void initService()
        {
            try
            {
                //获取mac地址
                wmi = new Wmi();
                ioHelper = new IOHelper();
                netHelper = new NetHelper(ioHelper.GetComByFriendlyName(getG3ModuleFriendlyName()), getG3ModuleComBaudRate(), getComTimeOut(), getG3NetworkCardName(), getG3ModuleApnName());
                hardwareHelper = new HardwareHelper(netHelper);

                mac = wmi.getMac();

                Log.WriteLog("MainService Start Service", LogType.Info);
                //启动HTTPServer
                startAllStateHttpServer();
            }
            catch (Exception e)
            {
                Log.WriteLog("MainService Init Service failed, error: " + e.Message, LogType.Error);
            }
        }
        //启动HTTPServer
        private void startAllStateHttpServer()
        {
            Dictionary<string, HttpServer.serviceDelegate> package = new Dictionary<string, HttpServer.serviceDelegate>();

            package.Add("get_hardware_id", delegate(HttpListenerContext httpListenerContext)
            {
                string callback = HttpServer._GET(httpListenerContext, "callback", "");
                if (string.IsNullOrEmpty(callback))
                {
                    return "{\"mac\":\"" + mac + "\"}";
                    //Log.WriteLog("MainService Get hardware id error: missing \"callback\" parameter", LogType.Error);
                }
                return callback + "({\"mac\":\"" + mac + "\"})";
            });

            package.Add("get_all_state_data", delegate(HttpListenerContext httpListenerContext)
            {
                string callback = HttpServer._GET(httpListenerContext, "callback", "");
                if (string.IsNullOrEmpty(callback))
                {
                    return hardwareHelper.getAllStatePostParameters();
                    //Log.WriteLog("MainService Get all state data error: missing \"callback\" parameter", LogType.Error);
                }
                return callback + "(" + hardwareHelper.getAllStatePostParameters() + ")";
            });
            httpServer = new HttpServer(getAllStateDataPort(), package);
        }
        protected void StartTimer()
        {
            Log.WriteLog("MainService HeartbeatTimer Start", LogType.Info);
            heartbeatTimer = new Timer();
            heartbeatTimer.Interval = getHeartbeatInterval();
            heartbeatTimer.Elapsed += new ElapsedEventHandler(heartbeatTimerEvent);
            heartbeatTimer.AutoReset = true;
            heartbeatTimer.Enabled = true;

            Log.WriteLog("MainService AllStateCollectTimer Start", LogType.Info);
            allStateCollectTimer = new Timer();
            allStateCollectTimer.Interval = getAllStateCollectInterval();
            allStateCollectTimer.Elapsed += new ElapsedEventHandler(allStateCollectTimerEvent);
            allStateCollectTimer.AutoReset = true;
            allStateCollectTimer.Enabled = true;

            Log.WriteLog("MainService WarningTimer Start", LogType.Info);
            warningTimer = new Timer();
            warningTimer.Interval = getWarningInterval();
            warningTimer.Elapsed += new ElapsedEventHandler(warningTimerEvent);
            warningTimer.AutoReset = true;
            warningTimer.Enabled = true;

            if (netHelper.getNetType() == NetType.NET3G)
            {
                Log.WriteLog("MainService NetCheckTimer Start", LogType.Info);
                netCheckTimer = new Timer();
                netCheckTimer.Interval = getNetCheckInterval();
                netCheckTimer.Elapsed += new ElapsedEventHandler(netCheckTimerEvent);
                netCheckTimer.AutoReset = true;
                netCheckTimer.Enabled = true;
            }
            else
            {
                Log.WriteLog("MainService NetHelper 3G Module is not exist, NetCheckTimer Start cancel", LogType.Info);
            }
        }
        protected void StopTimer()
        {
            heartbeatTimer.Stop();
            allStateCollectTimer.Stop();
            warningTimer.Stop();
            netCheckTimer.Stop();
            Log.WriteLog("MainService All Timer was Stoped", LogType.Info);
        }
        //心跳
        public void heartbeatTimerEvent(object source, System.Timers.ElapsedEventArgs args)
        {
            Log.WriteLog("MainService HearbeatTimer Ticking..., HeartbeatFailedTimes:" + _HeartbeatFailedTimes, LogType.Trace);
            //准备POST参数
            IDictionary<string, string> postParams = hardwareHelper.getHeartbeatPostParameters();
            //请求
            string response = HttpWebResponseUtility.CreatePostHttpResponse(getHeartbeatURL(), postParams, 2000, null, Encoding.UTF8, null);
            if (string.IsNullOrEmpty(response))
            {
                response = HttpWebResponseUtility.CreatePostHttpResponse(getHeartbeatBackupURL(), postParams, 2000, null, Encoding.UTF8, null);
            }
            //如果返回值为： {"errcode":"0","errmsg":"need set the clock","data":[]}，则需要对时操作
            if (!string.IsNullOrEmpty(response))
            {
                //如果是断线重连成功，则发送网络连通广播
                if (_HeartbeatFailedTimes != 0)
                {
                    SystemHelper.broadcastMessage(MessageType.LINK_STATE_MESSAGE.ToString(), 1, 0);
                    _HeartbeatFailedTimes = 0;
                }
                //是否为系统对时指令
                if (response.Contains("need set the clock"))
                {
                    watchClock();
                }
            }
            else
            {
                //设置网络状态为断线
                _HeartbeatFailedTimes ++;

                if (_HeartbeatFailedTimes >= 3)
                {
                    _HeartbeatFailedTimes = 0;
                    //断网通知
                    SystemHelper.broadcastMessage(MessageType.LINK_STATE_MESSAGE.ToString(), 0, 0);
                }
            }
        }
        //系统对时
        private void watchClock()
        {
            Log.WriteLog("MainService WatchClock Started", LogType.Info);
            //准备POST参数
            IDictionary<string, string> postParams = new Dictionary<string, string>();
            postParams.Add("reqTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
            //请求
            string response = HttpWebResponseUtility.CreatePostHttpResponse(getWatchURL(), postParams, 2000, null, Encoding.UTF8, null);
            if (string.IsNullOrEmpty(response))
            {
                response = HttpWebResponseUtility.CreatePostHttpResponse(getWatchBackupURL(), postParams, 2000, null, Encoding.UTF8, null);
            }
            //response {"errcode":"0","errmsg":"", "data":['req_time': '2015-8-25 00:10:09','res_time': '2015-8-25 00:10:09']}
            if (response != null)
            {
                JObject obj = JObject.Parse(response);
                if (obj["errcode"].ToString() == "0")
                {
                    JObject dataJson = (JObject)obj["data"];
                    try
                    {
                        DateTime reqTime = DateTime.Parse(dataJson["reqTime"].ToString());
                        DateTime resTime = DateTime.Parse(dataJson["resTime"].ToString());
                        setClock(reqTime, resTime);
                    }
                    catch (Exception e)
                    {
                        Log.WriteLog("MainService Parse json error: " + e.Message, LogType.Error);
                    }
                }
            }
        }
        //计算系统时间并设置系统时间
        private void setClock(DateTime reqTime, DateTime resTime)
        {
            //服务器当前时间 ＝ （resTime ＋ （系统当前时间－reqTime）／2 ）
            //若abs（系统当前时间 －resTime）>5 ,则设置系统时间为服务器当前时间
            DateTime currentTime = DateTime.Now;
            int reqTimeStamp = SystemHelper.ConvertDateTimeInt(reqTime);
            int resTimeStamp = SystemHelper.ConvertDateTimeInt(resTime);
            int currentTimeStamp = SystemHelper.ConvertDateTimeInt(currentTime);
            int serverTimeStamp = resTimeStamp + (currentTimeStamp - reqTimeStamp) / 2;

            Log.WriteLog("MainService Request Client Time:" + reqTime.ToString("yyyy-MM-dd HH:mm:ss"), LogType.Trace);
            Log.WriteLog("MainService Response Server Time:" + resTime.ToString("yyyy-MM-dd HH:mm:ss"), LogType.Trace);
            Log.WriteLog("MainService Current Client Time:" + currentTime.ToString("yyyy-MM-dd HH:mm:ss"), LogType.Trace);
            Log.WriteLog("MainService Compute Client Time:" + serverTimeStamp.ToString("yyyy-MM-dd HH:mm:ss"), LogType.Trace);

            Log.WriteLog("MainService If (Compute Client Time) - (Response Server Time):" + Math.Abs(currentTimeStamp - resTimeStamp) + " > 5 (s), need reset system clock" , LogType.Trace);

            if (Math.Abs(currentTimeStamp - resTimeStamp) > 5)
            {
                SystemHelper.setClock(SystemHelper.GetTime(serverTimeStamp.ToString()));
                Log.WriteLog("MainService Set System Clock:" + SystemHelper.GetTime(serverTimeStamp.ToString()).ToString("yyyy-MM-dd HH:mm:ss"), LogType.Info);
            }
        }
        //采集全状态数据
        public void allStateCollectTimerEvent(object source, ElapsedEventArgs args)
        {
            hardwareHelper.resetAllStateValue();
            hardwareHelper.collectAllStateValue();
        }
        //安防警报
        public void warningTimerEvent(object source, System.Timers.ElapsedEventArgs args)
        {
            Log.WriteLog("MainService WarningTimer Ticking...", LogType.Trace);
            List<string> ioCheckResult = new List<string>();
            bool needWarning = false;
            //获取安防警报检测IO列表
            ArrayList ioCheckList = ini.ReadValues("WarningIOCheckList", null);
            for (int i = 0; i < ioCheckList.Count; i++)
            {
                string ioCheckItem = ini.ReadValue("WarningIOCheckList", ioCheckList[i].ToString());
                string[] ioChecks = ioCheckItem.Split('/');
                if (ioChecks.Length < 3)
                {
                    Log.WriteLog("Config " + ioCheckItem + " is wrong, expect \"com1/4/1\"", LogType.Error);
                }
                string ioValue = "";
                if (isComIO(ioCheckItem))
                {
                    //属于COM方式
                    ioValue = ioHelper.checkCom(ioChecks[0], ioChecks[1]);
                }
                else
                {
                    //属于CPIO方式
                    ioValue = ioHelper.checkGpio(ioChecks[0], ioChecks[1]);
                }
                ioCheckResult.Add("{Name:" + ioCheckList[i].ToString() + ",Value:" + ioValue + ",RefValue:" + ioChecks[2] + "}");

                Log.WriteLog("MainService Check Warning IO Expect Value:" + ioChecks[2].ToString() + " Actual Value:" + ioValue.ToString(), LogType.Trace);
                if (ioValue != ioChecks[2])
                {
                    needWarning = true;
                    //发送安防警报广播
                    SystemHelper.broadcastMessage(MessageType.WARNING_MESSAGE.ToString(), i + 1);
                }
            }

            Log.WriteLog("MainService Should " + (needWarning ? "" : " Not") + " Send Warning", LogType.Trace);

            if (needWarning)
            {
                //准备POST参数
                IDictionary<string, string> postParams = new Dictionary<string, string>();
                postParams.Add("mac", mac);
                postParams.Add("ioCheckList", string.Join(",", ioCheckResult.ToArray()));
                //请求
                string response = HttpWebResponseUtility.CreatePostHttpResponse(getWarningURL(), postParams, 2000, null, Encoding.UTF8, null);
                if (string.IsNullOrEmpty(response))
                {
                    HttpWebResponseUtility.CreatePostHttpResponse(getWarningBackupURL(), postParams, 2000, null, Encoding.UTF8, null);
                }
            }
        }
        //检测网络状态
        public void netCheckTimerEvent(object source, System.Timers.ElapsedEventArgs args)
        {
            Log.WriteLog("MainService NetworkCheckTimer Ticking..., GetLinkStateTimes: " + _GetLinkStateTimes, LogType.Trace);
            _GetLinkStateTimes++;
            if (netHelper.getLinkState() == LinkState.OK && _HeartbeatFailedTimes < 3)
            {
                Log.WriteLog("MainService Network status is OK, send heartbeat message", LogType.Trace);
                //if (heartbeatTimer != null && heartbeatTimer.Enabled == false) heartbeatTimer.Start();
                _GetLinkStateTimes = 0;
            }
            
            if ((netHelper.getLinkState() == LinkState.NO || _HeartbeatFailedTimes >=3) && _GetLinkStateTimes >= 3)
            {
                _GetLinkStateTimes = 0;

                //if (heartbeatTimer != null && heartbeatTimer.Enabled) heartbeatTimer.Stop();

                Log.WriteLog("MainService Network status is NO", LogType.Trace);
                Log.WriteLog("MainService Network Recalling...", LogType.Trace);
                netHelper.call();
                System.Threading.Thread.Sleep(getNetRecallInterval());
            }
        }
        //开启调试日志
        private bool getShowTrace()
        {
            return ini.getStrConfig("Setting", "ShowTrace", "true") == "true" ? true : false;
        }
        //获取心跳上送周期
        private int getHeartbeatInterval()
        {
            return Math.Abs(ini.getIntConfig("Setting", "HeartbeatInterval", 10, 65535, 10) * 1000);
        }
        //获取心跳URL
        private string getHeartbeatURL()
        {
            return ini.getStrConfig("Setting", "HeartbeatURL", @"http://test.local/heartbeat");
        }
        //获取心跳备用URL
        private string getHeartbeatBackupURL()
        {
            return ini.getStrConfig("Setting", "HeartbeatBackupURL", @"http://test.local/heartbeat_backup");
        }
        //获取安防警报检测周期
        private int getWarningInterval()
        {
            return Math.Abs(ini.getIntConfig("Setting", "WarningInterval", 10, 65535, 10) * 1000);
        }
        //获取安防警报检测URL
        private string getWarningURL()
        {
            return ini.getStrConfig("Setting", "WarningURL", @"http://test.local/warning");
        }
        //获取安防警报检测备用URL
        private string getWarningBackupURL()
        {
            return ini.getStrConfig("Setting", "WarningBackupURL", @"http://test.local/warning_backup");
        }
        //获取对时URL
        private string getWatchURL()
        {
            return ini.getStrConfig("Setting", "WatchURL", @"http://test.local/watch");
        }
        //获取对时备用URL
        private string getWatchBackupURL()
        {
            return ini.getStrConfig("Setting", "WatchBackupURL", @"http://test.local/watch_backup");
        }
        private int getAllStateCollectInterval()
        {
            return Math.Abs(ini.getIntConfig("Setting", "AllStateCollectInterval", 10, 65535, 10) * 1000); 
        }
        private string getAllStateDataPort()
        {
            return ini.getStrConfig("Setting", "AllStateDataPort", "8010");
        }
        private int getNetCheckInterval()
        {
            return Math.Abs(ini.getIntConfig("Setting", "NetCheckInterval", 10, 65535, 10) * 1000);
        }
        private int getNetRecallInterval()
        {
            return Math.Abs(ini.getIntConfig("Setting", "NetRecallInterval", 0, 65535, 0) * 1000);
        }
        private string getG3NetworkCardName()
        {
            return ini.getStrConfig("Setting", "G3NetworkCardName", "HUAWEI Mobile Connect - Network Card");
        }
        private string getG3ModuleFriendlyName()
        {
            return ini.getStrConfig("Setting", "G3ModuleFriendlyName", "HUAWEI Mobile Connect - 3G PC UI Interface");
        }
        private int getG3ModuleComBaudRate()
        {
            return ini.getIntConfig("Setting", "G3ModuleComBaudRate", 9600, 115200, 115200);
        }
        private string getG3ModuleApnName()
        {
            return ini.getStrConfig("Setting", "G3ModuleApnName", "3GNET");
        }
        private int getComTimeOut()
        {
            return Math.Abs(ini.getIntConfig("Setting", "ComTimeOut", 10, 65535, 10) * 1000);
        }
        private bool isComIO(string ioItem)
        {
            return ioItem.ToLower().Contains("com");
        }
        //获取默认配置信息
        protected string getDefaultConfigIniContent()
        {
            return ";硬件与网络监控配置\r\n\r\n[Setting]\r\n;是否开启调试日志\r\nShowTrace=true"
                + "\r\n\r\n;日志保存地址(默认:" + currentDirectory + ")\r\nLogSavePath=" + currentDirectory
                + "\r\n\r\n;心跳上送时间间隔(10s-65535s)\r\nHeartbeatInterval=10\r\n;心跳上送URL\r\nHeartbeatURL=http://api.device.local/user/heartbeat\r\n;心跳上送备用URL\r\nHeartbeatBackupURL=http://api.device.local/user/heartbeat_backup"
                + "\r\n\r\n;安防警报IO检测时间间隔(10s-65535s)\r\nWarningInterval=10\r\n;安防警报IO检测上送URL\r\nWarningURL=http://api.device.local/user/warning\r\n;安防警报IO检测上送备用URL\r\nWarningBackupURL=http://api.device.local/user/warning_backup"
                + "\r\n\r\n;系统对时上送URL\r\nWatchURL=http://api.device.local/user/watch\r\n;系统对时上送备用URL\r\nWatchBackupURL=http://api.device.local/user/watch_backup"
                + "\r\n\r\n;全状态数据获取端口\r\nAllStateDataPort=8010\r\n;全状态数据采集周期（10-65535s），心跳上送时进行采集、计算，一个周期结束将重置所有数据\r\nAllStateCollectInterval=60"
                + "\r\n\r\n;网络诊断时间间隔(10s-65535s)\r\nNetCheckInterval=10\r\n;网络重拨时间间隔(0s-65535s)\r\nNetRecallInterval=5"
                + "\r\n;3G 网卡生成的本地网络连接名称\r\nG3NetworkCardName=HUAWEI Mobile Connect - Network Card\r\n;3G 模块COM口的友好名称（FriendlyName）\r\nG3ModuleFriendlyName=HUAWEI Mobile Connect - 3G PC UI Interface\r\n;3G模块COM 波特率\r\nG3ModuleComBaudRate=115200\r\n;3G模块 APN 名称\r\nG3ModuleApnName=3GNET"
                + "\r\n;COM口通讯超时时间(10s-65535s)\r\nComTimeOut=30"
                + "\r\n\r\n;安防警报IO检测列表\r\n[WarningIOCheckList]\r\n;IO名称=【通信方式】/【位数】/正常值\r\n;其中【通信方式】为 0x开头，则认为是GPIO方式，否则为COM方式\r\n;若【通信方式】为GPIO，则【位数】为0-7位， 若【通信方式】为COM，则可能的值有：8（CtsHolding）/4（DsrHolding）/1（CdHolding）"
                + "\r\ndoor=0x61/2/1\r\ndoor=0x61/1/1\r\nother=com1/4/1\r\nother=com1/8/1";
        }
        //获取当前服务程序地址
        private string getCurrentDirectory()
        {
            string defaultDir = currentDirectory;
            string currAppDir = "";
            try
            {
                RegistryHelper registryHelper = new RegistryHelper();
                currAppDir = registryHelper.GetRegistryData(Registry.LocalMachine, "SYSTEM\\CurrentControlSet\\Services\\" + serviceName, "ImagePath");
                currAppDir = currAppDir != "" ? currAppDir : defaultDir + "\\" + serviceName + ".exe";
                currAppDir = currAppDir.Replace("\"", "");
                currAppDir = currAppDir.Replace(@"%SystemRoot%\system32", Environment.GetFolderPath(Environment.SpecialFolder.System));

                if (currAppDir.IndexOf(" ") > 0)
                {
                    currAppDir = currAppDir.Substring(0, currAppDir.IndexOf(" "));
                }
                currAppDir = System.IO.Path.GetDirectoryName(currAppDir) + @"\";
            }
            catch (Exception e)
            {
                Log.WriteLog("MainService Get current directory:" + "SYSTEM\\CurrentControlSet\\Services\\" + serviceName + " error:" + e.Message, LogType.Error);
                currAppDir = defaultDir;
            }
            Log.WriteLog("MainService Get Current Directory:" + currAppDir, LogType.Trace);
            
            return currAppDir;
        }
    }

    public enum MessageType
    {
        WARNING_MESSAGE = 0,
        LINK_STATE_MESSAGE = 2
    }
}
